package com.changgou.seckill.service.impl;

import com.alibaba.fastjson.JSON;
import com.changgou.seckill.config.ConfirmMessageSender;
import com.changgou.seckill.config.RabbitMQConfig;
import com.changgou.seckill.dao.SeckillOrderMapper;
import com.changgou.seckill.pojo.SeckillGoods;
import com.changgou.seckill.pojo.SeckillOrder;
import com.changgou.seckill.service.SeckillOrderService;
import com.changgou.util.IdWorker;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * @author kfzhao
 * @version 1.0
 * @className SeckillOrderServiceImpl
 * @description
 * @date 2022/1/6 15:17
 */
@Service
public class SeckillOrderServiceImpl implements SeckillOrderService {
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private IdWorker idWorker;

    @Autowired
    private SeckillOrderMapper seckillOrderMapper;

    @Autowired
    private ConfirmMessageSender confirmMessageSender;

    public static final String SECKILL_GOODS_KEY = "seckill_goods_";

    public static final String SECKILL_GOODS_STOCK_COUNT_KEY = "seckill_goods_stock_count_key";


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean add(String time, Long id, String username) {
        /*
        1.获取redis中的商品信息与库存信息，并进行判断
        （判断是否存在，以及库存值是否够扣减）
        2.在redis中执行商品的库存数量预扣减操作，并获取扣减之后的库存值
        3.判断：如果扣减之后<=0，则删除redis中相应的商品信息与库存信息
        4.基于mq完成mysql的数据同步，进行异步下单并扣减库存（mysql中的数据）
         */

        // 防止用户恶意刷单
        String result = preventRepeatCommit(username, id);
        if ("fail".equals(result)) {
            return false;
        }

        // 防止相同商品重复购买
        SeckillOrder order = seckillOrderMapper.getOrderInfoByUsernameAndGoodsId(username, id);
        if (order != null) {
            return false;
        }


        // 获取商品信息
        SeckillGoods seckillGoods = (SeckillGoods) redisTemplate.boundHashOps(SECKILL_GOODS_KEY + time).get(id);
        // 获取库存信息
        String redisStock = (String) redisTemplate.opsForValue().get(SECKILL_GOODS_STOCK_COUNT_KEY + id);
        if (StringUtils.isEmpty(redisStock)) {
            return false;
        }
        int stock = Integer.parseInt(redisStock);
        if (seckillGoods == null || stock <= 0) {
            return false;
        }
        // 执行redis的预扣减库存，并获取到扣减之后的库存值
        // decrement()方法默认减一，也可以设置每次减多少（步长）increment()加
        // Lua脚本语言编写redis脚本时，也可以保证操作的原子性
        Long decrement = redisTemplate.opsForValue().decrement(SECKILL_GOODS_STOCK_COUNT_KEY + id);
        if (decrement <= 0) {
            // 扣减库存之后，当前商品已经没有库存了
            // 删除redis中的商品信息与库存信息
            redisTemplate.boundHashOps(SECKILL_GOODS_KEY + time).delete(id);
            redisTemplate.delete(SECKILL_GOODS_STOCK_COUNT_KEY + id);
        }
        // 向mq中发送消息（需要保证消息生产者对于消息不丢失）
        // 当前消息体：秒杀订单
        SeckillOrder seckillOrder = new SeckillOrder();
        seckillOrder.setId(idWorker.nextId());
        seckillOrder.setSeckillId(id);
        seckillOrder.setMoney(seckillGoods.getCostPrice());
        seckillOrder.setUserId(username);
        seckillOrder.setSellerId(seckillGoods.getSellerId());
        seckillOrder.setCreateTime(new Date());
        seckillOrder.setStatus("0");
        // 发送消息
        confirmMessageSender.sendMessage("", RabbitMQConfig.SECKILL_ORDER_QUEUE, JSON.toJSONString(seckillOrder));
        return true;
    }

    /**
     * 防止用户恶意刷单
     *
     * @param username 用户名
     * @param id       商品id
     * @return java.lang.String
     * @throws
     * @description
     * @author kfzhao
     * @date 2022/1/7 2:15
     */
    private String preventRepeatCommit(String username, Long id) {
        String redisKey = "seckill_user_" + username + "_id_" + id;
        long count = redisTemplate.opsForValue().increment(redisKey, 1);
        if (count == 1) {
            // 代表用户第一次访问
            // 对当前key设置5分钟的有效期，有效期内不能够再进行下单
            redisTemplate.expire(redisKey, 5, TimeUnit.MINUTES);
            return "success";
        }
        if (count > 1) {
            return "fail";
        }
        return "fail";
    }
}
